--- /dev/null
+#include "efi.h"
+#include <efi/efiprot.h>
+#include <public/xen.h>
+#include <xen/compile.h>
+#include <xen/ctype.h>
+#include <xen/dmi.h>
+#include <xen/init.h>
+#include <xen/keyhandler.h>
+#include <xen/lib.h>
+#include <xen/multiboot.h>
+#include <xen/pfn.h>
+#if EFI_PAGE_SIZE != PAGE_SIZE
+# error Cannot use xen/pfn.h here!
+#endif
+#include <xen/string.h>
+#include <xen/stringify.h>
+#include <xen/vga.h>
+#include <asm/e820.h>
+#include <asm/msr.h>
+#include <asm/processor.h>
+
+extern char start[];
+extern u32 cpuid_ext_features;
+
+union string {
+ CHAR16 *w;
+ char *s;
+ const char *cs;
+};
+
+struct file {
+ UINTN size;
+ union {
+ EFI_PHYSICAL_ADDRESS addr;
+ void *ptr;
+ };
+};
+
+static EFI_BOOT_SERVICES *__initdata efi_bs;
+static EFI_HANDLE __initdata efi_ih;
+
+static SIMPLE_TEXT_OUTPUT_INTERFACE __initdata *StdOut;
+static SIMPLE_TEXT_OUTPUT_INTERFACE __initdata *StdErr;
+
+static UINT32 __initdata mdesc_ver;
+
+static struct file __initdata cfg;
+static struct file __initdata kernel;
+static struct file __initdata ramdisk;
+static struct file __initdata xsm;
+
+static multiboot_info_t __initdata mbi = {
+ .flags = MBI_MODULES | MBI_LOADERNAME
+};
+static module_t __initdata mb_modules[3];
+
+static CHAR16 __initdata newline[] = L"\r\n";
+
+#define PrintStr(s) StdOut->OutputString(StdOut, s)
+#define PrintErr(s) StdErr->OutputString(StdErr, s)
+
+static CHAR16 *__init FormatDec(UINT64 Val, CHAR16 *Buffer)
+{
+ if ( Val >= 10 )
+ Buffer = FormatDec(Val / 10, Buffer);
+ *Buffer = (CHAR16)(L'0' + Val % 10);
+ return Buffer + 1;
+}
+
+static CHAR16 *__init FormatHex(UINT64 Val, UINTN Width, CHAR16 *Buffer)
+{
+ if ( Width > 1 || Val >= 0x10 )
+ Buffer = FormatHex(Val >> 4, Width ? Width - 1 : 0, Buffer);
+ *Buffer = (CHAR16)((Val &= 0xf) < 10 ? L'0' + Val : L'a' + Val - 10);
+ return Buffer + 1;
+}
+
+static void __init DisplayUint(UINT64 Val, INTN Width)
+{
+ CHAR16 PrintString[32], *end;
+
+ if (Width < 0)
+ end = FormatDec(Val, PrintString);
+ else
+ {
+ PrintStr(L"0x");
+ end = FormatHex(Val, Width, PrintString);
+ }
+ *end = 0;
+ PrintStr(PrintString);
+}
+
+static CHAR16 *__init wstrcpy(CHAR16 *d, const CHAR16 *s)
+{
+ CHAR16 *r = d;
+
+ while ( (*d++ = *s++) != 0 )
+ ;
+ return r;
+}
+
+static int __init wstrcmp(const CHAR16 *s1, const CHAR16 *s2)
+{
+ while ( *s1 && *s1 == *s2 )
+ {
+ ++s1;
+ ++s2;
+ }
+ return *s1 - *s2;
+}
+
+static int __init wstrncmp(const CHAR16 *s1, const CHAR16 *s2, UINTN n)
+{
+ while ( n && *s1 && *s1 == *s2 )
+ {
+ --n;
+ ++s1;
+ ++s2;
+ }
+ return n ? *s1 - *s2 : 0;
+}
+
+static CHAR16 *__init s2w(union string *str)
+{
+ const char *s = str->s;
+ CHAR16 *w;
+ void *ptr;
+
+ if ( efi_bs->AllocatePool(EfiLoaderData, (strlen(s) + 1) * sizeof(*w),
+ &ptr) != EFI_SUCCESS )
+ return NULL;
+
+ w = str->w = ptr;
+ do {
+ *w = *s++;
+ } while ( *w++ );
+
+ return str->w;
+}
+
+static char *__init w2s(const union string *str)
+{
+ const CHAR16 *w = str->w;
+ char *s = str->s;
+
+ do {
+ if ( *w > 0x007f )
+ return NULL;
+ *s = *w++;
+ } while ( *s++ );
+
+ return str->s;
+}
+
+static bool_t __init match_guid(const EFI_GUID *guid1, const EFI_GUID *guid2)
+{
+ return guid1->Data1 == guid2->Data1 &&
+ guid1->Data2 == guid2->Data2 &&
+ guid1->Data3 == guid2->Data3 &&
+ !memcmp(guid1->Data4, guid2->Data4, sizeof(guid1->Data4));
+}
+
+static void __init __attribute__((__noreturn__)) blexit(const CHAR16 *str)
+{
+ if ( str )
+ PrintStr((CHAR16 *)str);
+ PrintStr(newline);
+
+ if ( cfg.addr )
+ efi_bs->FreePages(cfg.addr, PFN_UP(cfg.size));
+ if ( kernel.addr )
+ efi_bs->FreePages(kernel.addr, PFN_UP(kernel.size));
+ if ( ramdisk.addr )
+ efi_bs->FreePages(ramdisk.addr, PFN_UP(ramdisk.size));
+ if ( xsm.addr )
+ efi_bs->FreePages(xsm.addr, PFN_UP(xsm.size));
+
+ efi_bs->Exit(efi_ih, EFI_SUCCESS, 0, NULL);
+ for( ; ; ); /* not reached */
+}
+
+/* generic routine for printing error messages */
+static void __init PrintErrMesg(const CHAR16 *mesg, EFI_STATUS ErrCode)
+{
+ StdOut = StdErr;
+ PrintErr((CHAR16 *)mesg);
+ PrintErr(L": ");
+
+ switch (ErrCode)
+ {
+ case EFI_NOT_FOUND:
+ mesg = L"Not found";
+ break;
+ case EFI_NO_MEDIA:
+ mesg = L"The device has no media";
+ break;
+ case EFI_MEDIA_CHANGED:
+ mesg = L"Media changed";
+ break;
+ case EFI_DEVICE_ERROR:
+ mesg = L"Device error";
+ break;
+ case EFI_VOLUME_CORRUPTED:
+ mesg = L"Volume corrupted";
+ break;
+ case EFI_ACCESS_DENIED:
+ mesg = L"Access denied";
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ mesg = L"Out of resources";
+ break;
+ case EFI_VOLUME_FULL:
+ mesg = L"Volume is full";
+ break;
+ default:
+ PrintErr(L"ErrCode: ");
+ DisplayUint(ErrCode, 0);
+ mesg = NULL;
+ break;
+ }
+ blexit(mesg);
+}
+
+static void __init place_string(u32 *addr, const char *s)
+{
+ static char *__initdata alloc = start;
+
+ if ( s && *s )
+ {
+ size_t len1 = strlen(s) + 1;
+ const char *old = (char *)(long)*addr;
+ size_t len2 = *addr ? strlen(old) + 1 : 0;
+
+ alloc -= len1 + len2;
+ /*
+ * Insert new string before already existing one. This is needed
+ * for options passed on the command line to override options from
+ * the configuration file.
+ */
+ memcpy(alloc, s, len1);
+ if ( *addr )
+ {
+ alloc[len1 - 1] = ' ';
+ memcpy(alloc + len1, old, len2);
+ }
+ }
+ *addr = (long)alloc;
+}
+
+static unsigned int __init get_argv(unsigned int argc, CHAR16 **argv,
+ CHAR16 *cmdline, UINTN cmdsize)
+{
+ CHAR16 *ptr = (CHAR16 *)(argv + argc + 1), *prev = NULL;
+ bool_t prev_sep = TRUE;
+
+ for ( ; cmdsize > sizeof(*cmdline) && *cmdline;
+ cmdsize -= sizeof(*cmdline), ++cmdline )
+ {
+ bool_t cur_sep = *cmdline == L' ' || *cmdline == L'\t';
+
+ if ( !prev_sep )
+ {
+ if ( cur_sep )
+ ++ptr;
+ else if ( argv )
+ {
+ *ptr = *cmdline;
+ *++ptr = 0;
+ }
+ }
+ else if ( !cur_sep )
+ {
+ if ( !argv )
+ ++argc;
+ else if ( prev && wstrcmp(prev, L"--") == 0 )
+ {
+ union string rest = { .w = cmdline };
+
+ --argv;
+ place_string(&mbi.cmdline, w2s(&rest));
+ break;
+ }
+ else
+ {
+ *argv++ = prev = ptr;
+ *ptr = *cmdline;
+ *++ptr = 0;
+ }
+ }
+ prev_sep = cur_sep;
+ }
+ if ( argv )
+ *argv = NULL;
+ return argc;
+}
+
+static EFI_FILE_HANDLE __init get_parent_handle(EFI_LOADED_IMAGE *loaded_image,
+ CHAR16 **leaf)
+{
+ static EFI_GUID __initdata fs_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
+ EFI_FILE_HANDLE dir_handle;
+ EFI_DEVICE_PATH *dp;
+ CHAR16 *pathend, *ptr;
+ EFI_STATUS ret;
+
+ do {
+ EFI_FILE_IO_INTERFACE *fio;
+
+ /* Get the file system interface. */
+ ret = efi_bs->HandleProtocol(loaded_image->DeviceHandle,
+ &fs_protocol, (void **)&fio);
+ if ( EFI_ERROR(ret) )
+ blexit(L"Couldn't obtain the File System Protocol Interface");
+ ret = fio->OpenVolume(fio, &dir_handle);
+ } while ( ret == EFI_MEDIA_CHANGED );
+ if ( ret != EFI_SUCCESS )
+ blexit(L"OpenVolume failure");
+
+#define buffer ((CHAR16 *)keyhandler_scratch)
+#define BUFFERSIZE sizeof(keyhandler_scratch)
+ for ( dp = loaded_image->FilePath, *buffer = 0;
+ DevicePathType(dp) != END_DEVICE_PATH_TYPE;
+ dp = (void *)dp + DevicePathNodeLength(dp) )
+ {
+ FILEPATH_DEVICE_PATH *fp;
+
+ if ( DevicePathType(dp) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType(dp) != MEDIA_FILEPATH_DP )
+ blexit(L"Unsupported device path component");
+
+ if ( *buffer )
+ {
+ EFI_FILE_HANDLE new_handle;
+
+ ret = dir_handle->Open(dir_handle, &new_handle, buffer,
+ EFI_FILE_MODE_READ, 0);
+ if ( ret != EFI_SUCCESS )
+ {
+ PrintErr(L"Open failed for ");
+ PrintErrMesg(buffer, ret);
+ }
+ dir_handle->Close(dir_handle);
+ dir_handle = new_handle;
+ }
+ fp = (void *)dp;
+ if ( BUFFERSIZE < DevicePathNodeLength(dp) -
+ sizeof(*dp) + sizeof(*buffer) )
+ blexit(L"Increase BUFFERSIZE");
+ memcpy(buffer, fp->PathName, DevicePathNodeLength(dp) - sizeof(*dp));
+ buffer[(DevicePathNodeLength(dp) - sizeof(*dp)) / sizeof(*buffer)] = 0;
+ }
+ for ( ptr = buffer, pathend = NULL; *ptr; ++ptr )
+ if ( *ptr == L'\\' )
+ pathend = ptr;
+ if ( pathend )
+ {
+ *pathend = 0;
+ *leaf = pathend + 1;
+ if ( *buffer )
+ {
+ EFI_FILE_HANDLE new_handle;
+
+ ret = dir_handle->Open(dir_handle, &new_handle, buffer,
+ EFI_FILE_MODE_READ, 0);
+ if ( ret != EFI_SUCCESS ) {
+ PrintErr(L"Open failed for ");
+ PrintErrMesg(buffer, ret);
+ }
+ dir_handle->Close(dir_handle);
+ dir_handle = new_handle;
+ }
+ }
+ else
+ *leaf = buffer;
+#undef BUFFERSIZE
+#undef buffer
+
+ return dir_handle;
+}
+
+static CHAR16 *__init point_tail(CHAR16 *fn)
+{
+ CHAR16 *tail = NULL;
+
+ for ( ; ; ++fn )
+ switch ( *fn )
+ {
+ case 0:
+ return tail;
+ case L'.':
+ case L'-':
+ case L'_':
+ tail = fn;
+ break;
+ }
+}
+
+static bool_t __init read_file(EFI_FILE_HANDLE dir_handle, CHAR16 *name,
+ struct file *file)
+{
+ EFI_FILE_HANDLE FileHandle = NULL;
+ UINT64 size;
+ EFI_STATUS ret;
+ CHAR16 *what = NULL;
+
+ if ( !name )
+ PrintErrMesg(L"No filename", EFI_OUT_OF_RESOURCES);
+ ret = dir_handle->Open(dir_handle, &FileHandle, name,
+ EFI_FILE_MODE_READ, 0);
+ if ( file == &cfg && ret == EFI_NOT_FOUND )
+ return 0;
+ if ( EFI_ERROR(ret) )
+ what = L"Open";
+ else
+ ret = FileHandle->SetPosition(FileHandle, -1);
+ if ( EFI_ERROR(ret) )
+ what = what ?: L"Seek";
+ else
+ ret = FileHandle->GetPosition(FileHandle, &size);
+ if ( EFI_ERROR(ret) )
+ what = what ?: L"Get size";
+ else
+ ret = FileHandle->SetPosition(FileHandle, 0);
+ if ( EFI_ERROR(ret) )
+ what = what ?: L"Seek";
+ else
+ {
+ file->addr = (EFI_PHYSICAL_ADDRESS)1 << (32 + PAGE_SHIFT);
+ ret = efi_bs->AllocatePages(AllocateMaxAddress, EfiLoaderData,
+ PFN_UP(size), &file->addr);
+ }
+ if ( EFI_ERROR(ret) )
+ {
+ file->addr = 0;
+ what = what ?: L"Allocation";
+ }
+ else
+ {
+ if ( file != &cfg )
+ {
+ PrintStr(name);
+ PrintStr(L": ");
+ DisplayUint(file->addr, 2 * sizeof(file->addr));
+ PrintStr(L"-");
+ DisplayUint(file->addr + size, 2 * sizeof(file->addr));
+ PrintStr(newline);
+ mb_modules[mbi.mods_count].mod_start = file->addr >> PAGE_SHIFT;
+ mb_modules[mbi.mods_count].mod_end = size;
+ ++mbi.mods_count;
+ }
+
+ file->size = size;
+ ret = FileHandle->Read(FileHandle, &file->size, file->ptr);
+ if ( !EFI_ERROR(ret) && file->size != size )
+ ret = EFI_ABORTED;
+ if ( EFI_ERROR(ret) )
+ what = L"Read";
+ }
+
+ if ( FileHandle )
+ FileHandle->Close(FileHandle);
+
+ if ( what )
+ {
+ PrintErr(what);
+ PrintErr(L" failed for ");
+ PrintErrMesg(name, ret);
+ }
+
+ return 1;
+}
+
+static void __init pre_parse(const struct file *cfg)
+{
+ char *ptr = cfg->ptr, *end = ptr + cfg->size;
+ bool_t start = 1, comment = 0;
+
+ for ( ; ptr < end; ++ptr )
+ {
+ if ( iscntrl(*ptr) )
+ {
+ comment = 0;
+ start = 1;
+ *ptr = 0;
+ }
+ else if ( comment || (start && isspace(*ptr)) )
+ *ptr = 0;
+ else if ( *ptr == '#' || (start && *ptr == ';') )
+ {
+ comment = 1;
+ *ptr = 0;
+ }
+ else
+ start = 0;
+ }
+ if ( cfg->size && end[-1] )
+ PrintStr(L"No newline at end of config file,"
+ " last line will be ignored.\r\n");
+}
+
+static char *__init get_value(const struct file *cfg, const char *section,
+ const char *item)
+{
+ char *ptr = cfg->ptr, *end = ptr + cfg->size;
+ size_t slen = section ? strlen(section) : 0, ilen = strlen(item);
+ bool_t match = !slen;
+
+ for ( ; ptr < end; ++ptr )
+ {
+ switch ( *ptr )
+ {
+ case 0:
+ continue;
+ case '[':
+ if ( !slen )
+ break;
+ if ( match )
+ return NULL;
+ match = strncmp(++ptr, section, slen) == 0 && ptr[slen] == ']';
+ break;
+ default:
+ if ( match && strncmp(ptr, item, ilen) == 0 && ptr[ilen] == '=' )
+ return ptr + ilen + 1;
+ break;
+ }
+ ptr += strlen(ptr);
+ }
+ return NULL;
+}
+
+static void __init split_value(char *s)
+{
+ while ( *s && isspace(*s) )
+ ++s;
+ place_string(&mb_modules[mbi.mods_count].string, s);
+ while ( *s && !isspace(*s) )
+ ++s;
+ *s = 0;
+}
+
+static int __init set_color(u32 mask, int bpp, u8 *pos, u8 *sz)
+{
+ if ( bpp < 0 )
+ return bpp;
+ if ( !mask )
+ return -EINVAL;
+ for ( *pos = 0; !(mask & 1); ++*pos )
+ mask >>= 1;
+ for ( *sz = 0; mask & 1; ++sz)
+ mask >>= 1;
+ if ( mask )
+ return -EINVAL;
+ return max(*pos + *sz, bpp);
+}
+
+#define PE_BASE_RELOC_ABS 0
+#define PE_BASE_RELOC_HIGHLOW 3
+#define PE_BASE_RELOC_DIR64 10
+
+extern const struct pe_base_relocs {
+ u32 rva;
+ u32 size;
+ u16 entries[];
+} __base_relocs_start[], __base_relocs_end[];
+
+static void __init relocate_image(unsigned long delta)
+{
+ const struct pe_base_relocs *base_relocs;
+
+ for ( base_relocs = __base_relocs_start; base_relocs < __base_relocs_end; )
+ {
+ unsigned int i, n;
+
+ n = (base_relocs->size - sizeof(*base_relocs)) /
+ sizeof(*base_relocs->entries);
+ for ( i = 0; i < n; ++i )
+ {
+ unsigned long addr = xen_phys_start + base_relocs->rva +
+ (base_relocs->entries[i] & 0xfff);
+
+ switch ( base_relocs->entries[i] >> 12 )
+ {
+ case PE_BASE_RELOC_ABS:
+ break;
+ case PE_BASE_RELOC_HIGHLOW:
+ if ( delta )
+ *(u32 *)addr += delta;
+ break;
+ case PE_BASE_RELOC_DIR64:
+ if ( delta )
+ *(u64 *)addr += delta;
+ break;
+ default:
+ blexit(L"Unsupported relocation type\r\n");
+ }
+ }
+ base_relocs = (const void *)(base_relocs->entries + i + (i & 1));
+ }
+}
+
+void EFIAPI __init __attribute__((__noreturn__))
+efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
+{
+ static EFI_GUID __initdata loaded_image_guid = LOADED_IMAGE_PROTOCOL;
+ static EFI_GUID __initdata gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_STATUS status;
+ unsigned int i, argc;
+ CHAR16 **argv, *file_name, *cfg_file_name = NULL;
+ UINTN cols, rows, depth, size, map_key, info_size, gop_mode = ~0;
+ EFI_HANDLE *handles = NULL;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = NULL;
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
+ EFI_FILE_HANDLE dir_handle;
+ union string section = { NULL }, name;
+ struct e820entry *e;
+ u64 efer;
+ bool_t base_video = 0, trampoline_okay = 0;
+
+ efi_ih = ImageHandle;
+ efi_bs = SystemTable->BootServices;
+ efi_rs = SystemTable->RuntimeServices;
+ efi_ct = SystemTable->ConfigurationTable;
+ efi_num_ct = SystemTable->NumberOfTableEntries;
+ efi_version = SystemTable->Hdr.Revision;
+ efi_fw_vendor = SystemTable->FirmwareVendor;
+ efi_fw_revision = SystemTable->FirmwareRevision;
+
+ StdOut = SystemTable->ConOut;
+ StdErr = SystemTable->StdErr ?: StdOut;
+
+ status = efi_bs->HandleProtocol(ImageHandle, &loaded_image_guid,
+ (void **)&loaded_image);
+ if ( status != EFI_SUCCESS )
+ PrintErrMesg(L"No Loaded Image Protocol", status);
+
+ xen_phys_start = (UINTN)loaded_image->ImageBase;
+ if ( (xen_phys_start + loaded_image->ImageSize - 1) >> 32 )
+ blexit(L"Xen must be loaded below 4Gb.\r\n");
+ if ( xen_phys_start & ((1 << L2_PAGETABLE_SHIFT) - 1) )
+ blexit(L"Xen must be loaded at a 2Mb boundary.\r\n");
+ trampoline_xen_phys_start = xen_phys_start;
+
+ /* Get the file system interface. */
+ dir_handle = get_parent_handle(loaded_image, &file_name);
+
+ argc = get_argv(0, NULL, loaded_image->LoadOptions,
+ loaded_image->LoadOptionsSize);
+ if ( argc > 0 &&
+ efi_bs->AllocatePool(EfiLoaderData,
+ (argc + 1) * sizeof(*argv) +
+ loaded_image->LoadOptionsSize,
+ (void **)&argv) == EFI_SUCCESS )
+ get_argv(argc, argv, loaded_image->LoadOptions,
+ loaded_image->LoadOptionsSize);
+ else
+ argc = 0;
+ for ( i = 1; i < argc; ++i )
+ {
+ CHAR16 *ptr = argv[i];
+
+ if ( !ptr )
+ break;
+ if ( *ptr == L'/' || *ptr == L'-' )
+ {
+ if ( wstrcmp(ptr + 1, L"basevideo") == 0 )
+ base_video = 1;
+ else if ( wstrncmp(ptr + 1, L"cfg=", 4) == 0 )
+ cfg_file_name = ptr + 5;
+ else if ( i + 1 < argc && wstrcmp(ptr + 1, L"cfg") == 0 )
+ cfg_file_name = argv[++i];
+ else if ( wstrcmp(ptr + 1, L"help") == 0 ||
+ (ptr[1] == L'?' && !ptr[2]) )
+ {
+ PrintStr(L"Xen EFI Loader options:\r\n");
+ PrintStr(L"-basevideo retain current video mode\r\n");
+ PrintStr(L"-cfg=<file> specify configuration file\r\n");
+ PrintStr(L"-help, -? display this help\r\n");
+ blexit(NULL);
+ }
+ else
+ {
+ PrintStr(L"WARNING: Unknown command line option '");
+ PrintStr(ptr);
+ PrintStr(L"' ignored\r\n");
+ }
+ }
+ else
+ section.w = ptr;
+ }
+
+ if ( !base_video )
+ {
+ unsigned int best;
+
+ for ( i = 0, size = 0, best = StdOut->Mode->Mode;
+ i < StdOut->Mode->MaxMode; ++i )
+ {
+ if ( StdOut->QueryMode(StdOut, i, &cols, &rows) == EFI_SUCCESS &&
+ cols * rows > size )
+ {
+ size = cols * rows;
+ best = i;
+ }
+ }
+ if ( best != StdOut->Mode->Mode )
+ StdOut->SetMode(StdOut, best);
+ }
+
+ PrintStr(L"Xen " __stringify(XEN_VERSION) "." __stringify(XEN_SUBVERSION)
+ XEN_EXTRAVERSION " (c/s " XEN_CHANGESET ") EFI loader\r\n");
+
+ relocate_image(0);
+
+ if ( StdOut->QueryMode(StdOut, StdOut->Mode->Mode,
+ &cols, &rows) == EFI_SUCCESS )
+ {
+ vga_console_info.video_type = XEN_VGATYPE_TEXT_MODE_3;
+ vga_console_info.u.text_mode_3.columns = cols;
+ vga_console_info.u.text_mode_3.rows = rows;
+ vga_console_info.u.text_mode_3.font_height = 16;
+ }
+
+ size = 0;
+ status = efi_bs->LocateHandle(ByProtocol, &gop_guid, NULL, &size, NULL);
+ if ( status == EFI_BUFFER_TOO_SMALL )
+ status = efi_bs->AllocatePool(EfiLoaderData, size, (void **)&handles);
+ if ( !EFI_ERROR(status) )
+ status = efi_bs->LocateHandle(ByProtocol, &gop_guid, NULL, &size,
+ handles);
+ if ( EFI_ERROR(status) )
+ size = 0;
+ for ( i = 0; i < size / sizeof(*handles); ++i )
+ {
+ status = efi_bs->HandleProtocol(handles[i], &gop_guid, (void **)&gop);
+ if ( EFI_ERROR(status) )
+ continue;
+ status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info);
+ if ( !EFI_ERROR(status) )
+ break;
+ }
+ if ( handles )
+ efi_bs->FreePool(handles);
+ if ( EFI_ERROR(status) )
+ gop = NULL;
+
+ /* Read and parse the config file. */
+ if ( !cfg_file_name )
+ {
+ CHAR16 *tail;
+
+ while ( (tail = point_tail(file_name)) != NULL )
+ {
+ wstrcpy(tail, L".cfg");
+ if ( read_file(dir_handle, file_name, &cfg) )
+ break;
+ *tail = 0;
+ }
+ if ( !tail )
+ blexit(L"No configuration file found\r\n");
+ PrintStr(L"Using configuration file '");
+ PrintStr(file_name);
+ PrintStr(L"'\r\n");
+ }
+ else if ( !read_file(dir_handle, cfg_file_name, &cfg) )
+ blexit(L"Configuration file not found\r\n");
+ pre_parse(&cfg);
+
+ if ( section.w )
+ w2s(§ion);
+ else
+ section.s = get_value(&cfg, "global", "default");
+
+ name.s = get_value(&cfg, section.s, "kernel");
+ if ( !name.s )
+ blexit(L"No Dom0 kernel image specified\r\n");
+ split_value(name.s);
+ read_file(dir_handle, s2w(&name), &kernel);
+ efi_bs->FreePool(name.w);
+
+ name.s = get_value(&cfg, section.s, "ramdisk");
+ if ( name.s )
+ {
+ split_value(name.s);
+ read_file(dir_handle, s2w(&name), &ramdisk);
+ efi_bs->FreePool(name.w);
+ }
+
+ name.s = get_value(&cfg, section.s, "xsm");
+ if ( name.s )
+ {
+ split_value(name.s);
+ read_file(dir_handle, s2w(&name), &xsm);
+ efi_bs->FreePool(name.w);
+ }
+
+ name.s = get_value(&cfg, section.s, "options");
+ if ( name.s )
+ place_string(&mbi.cmdline, name.s);
+ /* Insert image name last, as it gets prefixed to the other options. */
+ if ( argc )
+ {
+ name.w = *argv;
+ w2s(&name);
+ }
+ else
+ name.s = "xen";
+ place_string(&mbi.cmdline, name.s);
+
+ cols = rows = depth = 0;
+ if ( !base_video )
+ {
+ name.cs = get_value(&cfg, section.s, "video");
+ if ( !name.cs )
+ name.cs = get_value(&cfg, "global", "video");
+ if ( name.cs && !strncmp(name.cs, "gfx-", 4) )
+ {
+ cols = simple_strtoul(name.cs + 4, &name.cs, 10);
+ if ( *name.cs == 'x' )
+ rows = simple_strtoul(name.cs + 1, &name.cs, 10);
+ if ( *name.cs == 'x' )
+ depth = simple_strtoul(name.cs + 1, &name.cs, 10);
+ if ( *name.cs )
+ cols = rows = depth = 0;
+ }
+ }
+
+ efi_bs->FreePages(cfg.addr, PFN_UP(cfg.size));
+ cfg.addr = 0;
+
+ dir_handle->Close(dir_handle);
+
+ if ( gop && !base_video )
+ {
+ for ( i = size = 0; i < gop->Mode->MaxMode; ++i )
+ {
+ unsigned int bpp = 0;
+
+ status = gop->QueryMode(gop, i, &info_size, &mode_info);
+ if ( EFI_ERROR(status) )
+ continue;
+ switch ( mode_info->PixelFormat )
+ {
+ case PixelBitMask:
+ bpp = hweight32(mode_info->PixelInformation.RedMask |
+ mode_info->PixelInformation.GreenMask |
+ mode_info->PixelInformation.BlueMask);
+ break;
+ case PixelRedGreenBlueReserved8BitPerColor:
+ case PixelBlueGreenRedReserved8BitPerColor:
+ bpp = 24;
+ break;
+ default:
+ continue;
+ }
+ if ( cols == mode_info->HorizontalResolution &&
+ rows == mode_info->VerticalResolution &&
+ (!depth || bpp == depth) )
+ {
+ gop_mode = i;
+ break;
+ }
+ if ( !cols && !rows &&
+ mode_info->HorizontalResolution *
+ mode_info->VerticalResolution > size )
+ {
+ size = mode_info->HorizontalResolution *
+ mode_info->VerticalResolution;
+ gop_mode = i;
+ }
+ }
+ }
+
+ if ( mbi.cmdline )
+ mbi.flags |= MBI_CMDLINE;
+ /*
+ * These must not be initialized statically, since the value must
+ * not get relocated when processing base relocations below.
+ */
+ mbi.boot_loader_name = (long)"EFI";
+ mbi.mods_addr = (long)mb_modules;
+
+ place_string(&mbi.mem_upper, NULL);
+
+ /* XXX Collect EDD info. */
+ /* XXX Collect EDID info. */
+
+ if ( cpuid_eax(0x80000000) > 0x80000000 )
+ {
+ cpuid_ext_features = cpuid_edx(0x80000001);
+ boot_cpu_data.x86_capability[1] = cpuid_ext_features;
+ }
+
+ /* Obtain basic table pointers. */
+ for ( i = 0; i < efi_num_ct; ++i )
+ {
+ static EFI_GUID __initdata acpi2_guid = ACPI_20_TABLE_GUID;
+ static EFI_GUID __initdata acpi_guid = ACPI_TABLE_GUID;
+ static EFI_GUID __initdata smbios_guid = SMBIOS_TABLE_GUID;
+
+ if ( match_guid(&acpi2_guid, &efi_ct[i].VendorGuid) )
+ efi.acpi20 = (long)efi_ct[i].VendorTable;
+ if ( match_guid(&acpi_guid, &efi_ct[i].VendorGuid) )
+ efi.acpi = (long)efi_ct[i].VendorTable;
+ if ( match_guid(&smbios_guid, &efi_ct[i].VendorGuid) )
+ efi.smbios = (long)efi_ct[i].VendorTable;
+ }
+
+ if (efi.smbios != EFI_INVALID_TABLE_ADDR)
+ dmi_efi_get_table((void *)(long)efi.smbios);
+
+ /* Allocate space for trampoline (in first Mb). */
+ cfg.addr = BOOT_TRAMPOLINE;
+ cfg.size = trampoline_end - trampoline_start;
+ status = efi_bs->AllocatePages(AllocateAddress, EfiLoaderData,
+ PFN_UP(cfg.size), &cfg.addr);
+ if ( EFI_ERROR(status) )
+ {
+ cfg.addr = 0;
+ PrintErr(L"Note: Trampoline area is in use\r\n");
+ }
+
+ /* Initialise L2 identity-map and xen page table entries (16MB). */
+ for ( i = 0; i < 8; ++i )
+ {
+ unsigned int slot = (xen_phys_start >> L2_PAGETABLE_SHIFT) + i;
+ paddr_t addr = slot << L2_PAGETABLE_SHIFT;
+
+ l2_identmap[i] = l2e_from_paddr(i << L2_PAGETABLE_SHIFT,
+ PAGE_HYPERVISOR|_PAGE_PSE);
+ l2_identmap[slot] = l2e_from_paddr(addr, PAGE_HYPERVISOR|_PAGE_PSE);
+ l2_xenmap[i] = l2e_from_paddr(addr, PAGE_HYPERVISOR|_PAGE_PSE);
+ slot &= L2_PAGETABLE_ENTRIES - 1;
+ l2_bootmap[slot] = l2e_from_paddr(addr, __PAGE_HYPERVISOR|_PAGE_PSE);
+ }
+ /* Initialise L3 identity-map page directory entries. */
+ for ( i = 0; i < ARRAY_SIZE(l2_identmap) / L2_PAGETABLE_ENTRIES; ++i )
+ l3_identmap[i] = l3e_from_paddr((UINTN)(l2_identmap +
+ i * L2_PAGETABLE_ENTRIES),
+ __PAGE_HYPERVISOR);
+ /* Initialise L3 xen-map page directory entry. */
+ l3_xenmap[l3_table_offset(XEN_VIRT_START)] =
+ l3e_from_paddr((UINTN)l2_xenmap, __PAGE_HYPERVISOR);
+ /* Initialise L3 boot-map page directory entries. */
+ l3_bootmap[l3_table_offset(xen_phys_start)] =
+ l3e_from_paddr((UINTN)l2_bootmap, __PAGE_HYPERVISOR);
+ l3_bootmap[l3_table_offset(xen_phys_start + (8 << L2_PAGETABLE_SHIFT) - 1)] =
+ l3e_from_paddr((UINTN)l2_bootmap, __PAGE_HYPERVISOR);
+ /* Hook identity-map, xen-map, and boot-map L3 tables into PML4. */
+ idle_pg_table[0] = l4e_from_paddr((UINTN)l3_bootmap, __PAGE_HYPERVISOR);
+ idle_pg_table[l4_table_offset(DIRECTMAP_VIRT_START)] =
+ l4e_from_paddr((UINTN)l3_identmap, __PAGE_HYPERVISOR);
+ idle_pg_table[l4_table_offset(XEN_VIRT_START)] =
+ l4e_from_paddr((UINTN)l3_xenmap, __PAGE_HYPERVISOR);
+ /* Initialize 4kB mappings of first 2MB of memory. */
+ for ( i = 0; i < L1_PAGETABLE_ENTRIES; ++i )
+ {
+ unsigned int attr = PAGE_HYPERVISOR|MAP_SMALL_PAGES;
+
+ /* VGA hole (0xa0000-0xc0000) should be mapped UC. */
+ if ( i >= 0xa0 && i < 0xc0 )
+ attr |= _PAGE_PCD;
+ l1_identmap[i] = l1e_from_pfn(i, attr);
+ }
+ l2_identmap[0] = l2e_from_paddr((UINTN)l1_identmap, __PAGE_HYPERVISOR);
+
+ if ( gop )
+ {
+ int bpp = 0;
+
+ /* Set graphics mode. */
+ if ( gop_mode < gop->Mode->MaxMode && gop_mode != gop->Mode->Mode )
+ gop->SetMode(gop, gop_mode);
+
+ /* Get graphics and frame buffer info. */
+ status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info);
+ if ( !EFI_ERROR(status) )
+ switch ( mode_info->PixelFormat )
+ {
+ case PixelRedGreenBlueReserved8BitPerColor:
+ vga_console_info.u.vesa_lfb.red_pos = 0;
+ vga_console_info.u.vesa_lfb.red_size = 8;
+ vga_console_info.u.vesa_lfb.green_pos = 8;
+ vga_console_info.u.vesa_lfb.green_size = 8;
+ vga_console_info.u.vesa_lfb.blue_pos = 16;
+ vga_console_info.u.vesa_lfb.blue_size = 8;
+ vga_console_info.u.vesa_lfb.rsvd_pos = 24;
+ vga_console_info.u.vesa_lfb.rsvd_size = 8;
+ bpp = 32;
+ break;
+ case PixelBlueGreenRedReserved8BitPerColor:
+ vga_console_info.u.vesa_lfb.red_pos = 16;
+ vga_console_info.u.vesa_lfb.red_size = 8;
+ vga_console_info.u.vesa_lfb.green_pos = 8;
+ vga_console_info.u.vesa_lfb.green_size = 8;
+ vga_console_info.u.vesa_lfb.blue_pos = 0;
+ vga_console_info.u.vesa_lfb.blue_size = 8;
+ vga_console_info.u.vesa_lfb.rsvd_pos = 24;
+ vga_console_info.u.vesa_lfb.rsvd_size = 8;
+ bpp = 32;
+ break;
+ case PixelBitMask:
+ bpp = set_color(mode_info->PixelInformation.RedMask, bpp,
+ &vga_console_info.u.vesa_lfb.red_pos,
+ &vga_console_info.u.vesa_lfb.red_size);
+ bpp = set_color(mode_info->PixelInformation.GreenMask, bpp,
+ &vga_console_info.u.vesa_lfb.green_pos,
+ &vga_console_info.u.vesa_lfb.green_size);
+ bpp = set_color(mode_info->PixelInformation.BlueMask, bpp,
+ &vga_console_info.u.vesa_lfb.blue_pos,
+ &vga_console_info.u.vesa_lfb.blue_size);
+ bpp = set_color(mode_info->PixelInformation.ReservedMask, bpp,
+ &vga_console_info.u.vesa_lfb.rsvd_pos,
+ &vga_console_info.u.vesa_lfb.rsvd_size);
+ if ( bpp > 0 )
+ break;
+ /* fall through */
+ default:
+ PrintErr(L"Current graphics mode is unsupported!");
+ status = EFI_UNSUPPORTED;
+ break;
+ }
+ if ( !EFI_ERROR(status) )
+ {
+ vga_console_info.video_type = XEN_VGATYPE_EFI_LFB;
+ vga_console_info.u.vesa_lfb.gbl_caps = 2; /* possibly non-VGA */
+ vga_console_info.u.vesa_lfb.width =
+ mode_info->HorizontalResolution;
+ vga_console_info.u.vesa_lfb.height = mode_info->VerticalResolution;
+ vga_console_info.u.vesa_lfb.bits_per_pixel = bpp;
+ vga_console_info.u.vesa_lfb.bytes_per_line =
+ (mode_info->PixelsPerScanLine * bpp + 7) >> 3;
+ vga_console_info.u.vesa_lfb.lfb_base = gop->Mode->FrameBufferBase;
+ vga_console_info.u.vesa_lfb.lfb_size =
+ (gop->Mode->FrameBufferSize + 0xffff) >> 16;
+ }
+ }
+
+ status = efi_bs->GetMemoryMap(&efi_memmap_size, NULL, &map_key,
+ &efi_mdesc_size, &mdesc_ver);
+ mbi.mem_upper -= efi_memmap_size;
+ mbi.mem_upper &= -__alignof__(EFI_MEMORY_DESCRIPTOR);
+ if ( mbi.mem_upper < xen_phys_start )
+ blexit(L"Out of static memory\r\n");
+ efi_memmap = (void *)(long)mbi.mem_upper;
+ status = efi_bs->GetMemoryMap(&efi_memmap_size, efi_memmap, &map_key,
+ &efi_mdesc_size, &mdesc_ver);
+ if ( EFI_ERROR(status) )
+ blexit(L"Cannot obtain memory map\r\n");
+
+ /* Populate E820 table and check trampoline area availability. */
+ e = e820map - 1;
+ for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
+ {
+ EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
+ u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
+ u32 type;
+
+ switch ( desc->Type )
+ {
+ default:
+ type = E820_RESERVED;
+ break;
+ case EfiConventionalMemory:
+ case EfiLoaderCode:
+ case EfiLoaderData:
+ case EfiBootServicesCode:
+ case EfiBootServicesData:
+ if ( desc->Attribute & EFI_MEMORY_WB )
+ type = E820_RAM;
+ else
+ case EfiUnusableMemory:
+ type = E820_UNUSABLE;
+ break;
+ case EfiACPIReclaimMemory:
+ type = E820_ACPI;
+ break;
+ case EfiACPIMemoryNVS:
+ type = E820_NVS;
+ break;
+ }
+ if ( e820nr && type == e->type &&
+ desc->PhysicalStart == e->addr + e->size )
+ e->size += len;
+ else if ( !len || e820nr >= E820MAX )
+ continue;
+ else
+ {
+ ++e;
+ e->addr = desc->PhysicalStart;
+ e->size = len;
+ e->type = type;
+ ++e820nr;
+ }
+ if ( type == E820_RAM && e->addr <= BOOT_TRAMPOLINE &&
+ e->addr + e->size >= BOOT_TRAMPOLINE + cfg.size )
+ trampoline_okay = 1;
+ }
+
+ if ( !trampoline_okay )
+ blexit(L"Trampoline area unavailable\r\n");
+
+ status = efi_bs->ExitBootServices(ImageHandle, map_key);
+ if ( EFI_ERROR(status) )
+ PrintErrMesg(L"Cannot exit boot services", status);
+
+ /* Adjust pointers into EFI. */
+ efi_ct = (void *)efi_ct + DIRECTMAP_VIRT_START;
+#if 0 /* Only needed when using virtual mode (see efi_init_memory()). */
+ efi_rs = (void *)efi_rs + DIRECTMAP_VIRT_START;
+#endif
+ efi_memmap = (void *)efi_memmap + DIRECTMAP_VIRT_START;
+ efi_fw_vendor = (void *)efi_fw_vendor + DIRECTMAP_VIRT_START;
+
+ relocate_image(__XEN_VIRT_START - xen_phys_start);
+ memcpy((void *)(long)BOOT_TRAMPOLINE, trampoline_start, cfg.size);
+
+ /* Set system registers and transfer control. */
+ asm volatile("pushq $0\n\tpopfq");
+ rdmsrl(MSR_EFER, efer);
+ efer |= EFER_SCE;
+ if ( cpuid_ext_features & (1 << (X86_FEATURE_NX & 0x1f)) )
+ efer |= EFER_NX;
+ wrmsrl(MSR_EFER, efer);
+ write_cr0(X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | X86_CR0_NE | X86_CR0_WP |
+ X86_CR0_AM | X86_CR0_PG);
+ asm volatile ( "mov %[cr4], %%cr4\n\t"
+ "mov %[cr3], %%cr3\n\t"
+ "movabs $__start_xen, %[rip]\n\t"
+ "lidt idt_descr(%%rip)\n\t"
+ "lgdt gdt_descr(%%rip)\n\t"
+ "mov stack_start(%%rip), %%rsp\n\t"
+ "mov %[ds], %%ss\n\t"
+ "mov %[ds], %%ds\n\t"
+ "mov %[ds], %%es\n\t"
+ "mov %[ds], %%fs\n\t"
+ "mov %[ds], %%gs\n\t"
+ "movl %[cs], 8(%%rsp)\n\t"
+ "mov %[rip], (%%rsp)\n\t"
+ "lretq %[stkoff]-16"
+ : [rip] "=&r" (efer/* any dead 64-bit variable */)
+ : [cr3] "r" (idle_pg_table),
+ [cr4] "r" (mmu_cr4_features),
+ [cs] "ir" (__HYPERVISOR_CS),
+ [ds] "r" (__HYPERVISOR_DS),
+ [stkoff] "i" (STACK_SIZE - sizeof(struct cpu_info)),
+ "D" (&mbi)
+ : "memory" );
+ for( ; ; ); /* not reached */
+}
+
+void __init efi_init_memory(void)
+{
+ unsigned int i;
+
+ printk(XENLOG_INFO "EFI memory map:\n");
+ for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
+ {
+ EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
+ u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
+ unsigned long smfn, emfn;
+ unsigned int prot = PAGE_HYPERVISOR;
+
+ printk(XENLOG_INFO " %013" PRIx64 "-%013" PRIx64
+ " type=%u attr=%016" PRIx64 "\n",
+ desc->PhysicalStart, desc->PhysicalStart + len - 1,
+ desc->Type, desc->Attribute);
+
+ if ( !(desc->Attribute & EFI_MEMORY_RUNTIME) )
+ continue;
+
+ smfn = PFN_DOWN(desc->PhysicalStart);
+ emfn = PFN_UP(desc->PhysicalStart + len);
+
+ desc->VirtualStart = 0xBAAADUL << (EFI_PAGE_SHIFT + BITS_PER_LONG - 32);
+
+ if ( desc->Attribute & EFI_MEMORY_WB )
+ /* nothing */;
+ else if ( desc->Attribute & EFI_MEMORY_WT )
+ prot |= _PAGE_PWT | MAP_SMALL_PAGES;
+ else if ( desc->Attribute & EFI_MEMORY_WC )
+ prot |= _PAGE_PAT | MAP_SMALL_PAGES;
+ else if ( desc->Attribute & (EFI_MEMORY_UC | EFI_MEMORY_UCE) )
+ prot |= _PAGE_PWT | _PAGE_PCD | MAP_SMALL_PAGES;
+ else
+ {
+ printk(XENLOG_ERR "Unknown cachability for MFNs %#lx-%#lx\n",
+ smfn, emfn - 1);
+ continue;
+ }
+
+ if ( desc->Attribute & EFI_MEMORY_WP )
+ prot &= _PAGE_RW;
+ if ( desc->Attribute & EFI_MEMORY_XP )
+ prot |= _PAGE_NX_BIT;
+
+ if ( pfn_to_pdx(emfn - 1) < (DIRECTMAP_SIZE >> PAGE_SHIFT) &&
+ !(smfn & pfn_hole_mask) &&
+ !((smfn ^ (emfn - 1)) & ~pfn_pdx_bottom_mask) )
+ {
+ if ( map_pages_to_xen((unsigned long)mfn_to_virt(smfn),
+ smfn, emfn - smfn, prot) == 0 )
+ desc->VirtualStart =
+ (unsigned long)maddr_to_virt(desc->PhysicalStart);
+ else
+ printk(XENLOG_ERR "Could not map MFNs %#lx-%#lx\n",
+ smfn, emfn - 1);
+ }
+ else
+ {
+ /* XXX allocate e.g. down from FIXADDR_START */
+ printk(XENLOG_ERR "No mapping for MFNs %#lx-%#lx\n",
+ smfn, emfn - 1);
+ }
+ }
+
+#if 0 /* Incompatible with kexec. */
+ efi_rs->SetVirtualAddressMap(efi_memmap_size, efi_mdesc_size,
+ mdesc_ver, efi_memmap);
+#endif
+}
--- /dev/null
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+struct mz_hdr {
+ uint16_t signature;
+#define MZ_SIGNATURE 0x5a4d
+ uint16_t last_page_size;
+ uint16_t page_count;
+ uint16_t relocation_count;
+ uint16_t header_paras;
+ uint16_t min_paras;
+ uint16_t max_paras;
+ uint16_t entry_ss;
+ uint16_t entry_sp;
+ uint16_t checksum;
+ uint16_t entry_ip;
+ uint16_t entry_cs;
+ uint16_t relocations;
+ uint16_t overlay;
+ uint8_t reserved[32];
+ uint32_t extended_header_base;
+};
+
+struct pe_hdr {
+ uint32_t signature;
+#define PE_SIGNATURE 0x00004550
+ uint16_t cpu;
+ uint16_t section_count;
+ int32_t timestamp;
+ uint32_t symbols_file_offset;
+ uint32_t symbol_count;
+ uint16_t opt_hdr_size;
+ uint16_t flags;
+ struct {
+ uint16_t magic;
+#define PE_MAGIC_EXE32 0x010b
+#define PE_MAGIC_EXE32PLUS 0x020b
+ uint8_t linker_major, linker_minor;
+ uint32_t code_size, data_size, bss_size;
+ uint32_t entry_rva, code_rva, data_rva;
+ } opt_hdr;
+};
+
+#define PE_PAGE_SIZE 0x1000
+
+#define PE_BASE_RELOC_ABS 0
+#define PE_BASE_RELOC_HIGHLOW 3
+#define PE_BASE_RELOC_DIR64 10
+
+struct coff_section {
+ char name[8];
+ uint32_t size;
+ uint32_t rva;
+ uint32_t file_size;
+ uint32_t file_offset;
+ uint32_t relocation_file_offset;
+ uint32_t line_number_file_offset;
+ uint16_t relocation_count;
+ uint16_t line_number_count;
+ uint32_t flags;
+#define COFF_SECTION_BSS 0x00000080
+#define COFF_SECTION_DISCARDABLE 0x02000000
+};
+
+static void usage(const char *cmd, int rc)
+{
+ fprintf(rc ? stderr : stdout,
+ "Usage: %s <image1> <image2>\n",
+ cmd);
+ exit(rc);
+}
+
+static unsigned int load(const char *name, int *handle,
+ struct coff_section **sections,
+ uint_fast64_t *image_base,
+ uint32_t *image_size,
+ unsigned int *width)
+{
+ int in = open(name, O_RDONLY);
+ struct mz_hdr mz_hdr;
+ struct pe_hdr pe_hdr;
+ uint32_t base;
+
+ if ( in < 0 ||
+ read(in, &mz_hdr, sizeof(mz_hdr)) != sizeof(mz_hdr) )
+ {
+ perror(name);
+ exit(2);
+ }
+ if ( mz_hdr.signature != MZ_SIGNATURE ||
+ mz_hdr.relocations < sizeof(mz_hdr) ||
+ !mz_hdr.extended_header_base )
+ {
+ fprintf(stderr, "%s: Wrong DOS file format\n", name);
+ exit(2);
+ }
+
+ if ( lseek(in, mz_hdr.extended_header_base, SEEK_SET) < 0 ||
+ read(in, &pe_hdr, sizeof(pe_hdr)) != sizeof(pe_hdr) ||
+ read(in, &base, sizeof(base)) != sizeof(base) ||
+ /*
+ * Luckily the image size field lives at the
+ * same offset for both formats.
+ */
+ lseek(in, 24, SEEK_CUR) < 0 ||
+ read(in, image_size, sizeof(*image_size)) != sizeof(*image_size) )
+ {
+ perror(name);
+ exit(3);
+ }
+ switch ( (pe_hdr.signature == PE_SIGNATURE &&
+ pe_hdr.opt_hdr_size > sizeof(pe_hdr.opt_hdr)) *
+ pe_hdr.opt_hdr.magic )
+ {
+ case PE_MAGIC_EXE32:
+ *width = 32;
+ *image_base = base;
+ break;
+ case PE_MAGIC_EXE32PLUS:
+ *width = 64;
+ *image_base = ((uint64_t)base << 32) | pe_hdr.opt_hdr.data_rva;
+ break;
+ default:
+ fprintf(stderr, "%s: Wrong PE file format\n", name);
+ exit(3);
+ }
+
+ *sections = malloc(pe_hdr.section_count * sizeof(**sections));
+ if ( !*sections )
+ {
+ perror(NULL);
+ exit(4);
+ }
+ if ( lseek(in,
+ mz_hdr.extended_header_base + offsetof(struct pe_hdr, opt_hdr) +
+ pe_hdr.opt_hdr_size,
+ SEEK_SET) < 0 ||
+ read(in, *sections, pe_hdr.section_count * sizeof(**sections)) !=
+ pe_hdr.section_count * sizeof(**sections) )
+ {
+ perror(name);
+ exit(4);
+ }
+
+ *handle = in;
+
+ return pe_hdr.section_count;
+}
+
+static long page_size;
+
+static const void *map_section(const struct coff_section *sec, int in,
+ const char *name)
+{
+ const char *ptr;
+ unsigned long offs;
+
+ if ( !page_size )
+ page_size = sysconf(_SC_PAGESIZE);
+ offs = sec->file_offset & (page_size - 1);
+
+ ptr = mmap(0, offs + sec->file_size, PROT_READ, MAP_PRIVATE, in,
+ sec->file_offset - offs);
+ if ( ptr == MAP_FAILED )
+ {
+ perror(name);
+ exit(6);
+ }
+
+ return ptr + offs;
+}
+
+static void unmap_section(const void *ptr, const struct coff_section *sec)
+{
+ unsigned long offs = sec->file_offset & (page_size - 1);
+
+ munmap((char *)ptr - offs, offs + sec->file_size);
+}
+
+static void diff_sections(const unsigned char *ptr1, const unsigned char *ptr2,
+ const struct coff_section *sec,
+ int_fast64_t diff, unsigned int width,
+ uint_fast64_t base, uint_fast64_t end)
+{
+ static uint_fast32_t cur_rva, reloc_size;
+ unsigned int disp = 0;
+ uint_fast32_t i;
+
+ if ( !sec )
+ {
+ reloc_size += reloc_size & 2;
+ if ( reloc_size )
+ printf("\t.balign 4\n"
+ "\t.equ rva_%08" PRIxFAST32 "_relocs, %#08" PRIxFAST32 "\n",
+ cur_rva, reloc_size);
+ return;
+ }
+
+ while ( !(diff & (((int_fast64_t)1 << ((disp + 1) * CHAR_BIT)) - 1)) )
+ ++disp;
+
+ for ( i = 0; i < sec->file_size; ++i )
+ {
+ uint_fast32_t rva;
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ } val1, val2;
+ int_fast64_t delta;
+ unsigned int reloc = (width == 4 ? PE_BASE_RELOC_HIGHLOW :
+ PE_BASE_RELOC_DIR64);
+
+ if ( ptr1[i] == ptr2[i] )
+ continue;
+
+ if ( i < disp || i + width - disp > sec->file_size )
+ {
+ fprintf(stderr,
+ "Bogus difference at %s:%08" PRIxFAST32 "\n",
+ sec->name, i);
+ exit(3);
+ }
+
+ memcpy(&val1, ptr1 + i - disp, width);
+ memcpy(&val2, ptr2 + i - disp, width);
+ delta = width == 4 ? val2.u32 - val1.u32 : val2.u64 - val1.u64;
+ if ( delta != diff )
+ {
+ fprintf(stderr,
+ "Difference at %s:%08" PRIxFAST32 " is %#" PRIxFAST64
+ " (expected %#" PRIxFAST64 ")\n",
+ sec->name, i, delta, diff);
+ continue;
+ }
+ if ( width == 8 && (val1.u64 < base || val1.u64 > end) )
+ reloc = PE_BASE_RELOC_HIGHLOW;
+
+ rva = (sec->rva + i - disp) & ~(PE_PAGE_SIZE - 1);
+ if ( rva > cur_rva )
+ {
+ reloc_size += reloc_size & 2;
+ if ( reloc_size )
+ printf("\t.equ rva_%08" PRIxFAST32 "_relocs,"
+ " %#08" PRIxFAST32 "\n",
+ cur_rva, reloc_size);
+ printf("\t.balign 4\n"
+ "\t.long %#08" PRIxFAST32 ","
+ " rva_%08" PRIxFAST32 "_relocs\n",
+ rva, rva);
+ cur_rva = rva;
+ reloc_size = 8;
+ }
+ else if ( rva != cur_rva )
+ {
+ fprintf(stderr,
+ "Cannot handle decreasing RVA (at %s:%08" PRIxFAST32 ")\n",
+ sec->name, i);
+ exit(3);
+ }
+
+ printf("\t.word (%u << 12) | 0x%03" PRIxFAST32 "\n",
+ reloc, sec->rva + i - disp - rva);
+ reloc_size += 2;
+ i += width - disp - 1;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int in1, in2;
+ unsigned int i, nsec, width1, width2;
+ uint_fast64_t base1, base2;
+ uint32_t size1, size2;
+ struct coff_section *sec1, *sec2;
+
+ if ( argc == 1 ||
+ !strcmp(argv[1], "-?") ||
+ !strcmp(argv[1], "-h") ||
+ !strcmp(argv[1], "--help") )
+ usage(*argv, argc == 1);
+
+ if ( argc != 3 )
+ usage(*argv, 1);
+
+ nsec = load(argv[1], &in1, &sec1, &base1, &size1, &width1);
+ if ( nsec != load(argv[2], &in2, &sec2, &base2, &size2, &width2) )
+ {
+ fputs("Mismatched section counts\n", stderr);
+ return 5;
+ }
+ if ( width1 != width2 )
+ {
+ fputs("Mismatched image types\n", stderr);
+ return 5;
+ }
+ width1 >>= 3;
+ if ( base1 == base2 )
+ {
+ fputs("Images must have different base addresses\n", stderr);
+ return 5;
+ }
+ if ( size1 != size2 )
+ {
+ fputs("Images must have identical sizes\n", stderr);
+ return 5;
+ }
+
+ puts("\t.section .reloc, \"a\", @progbits\n"
+ "\t.balign 4\n"
+ "\t.globl __base_relocs_start, __base_relocs_end\n"
+ "__base_relocs_start:");
+
+ for ( i = 0; i < nsec; ++i )
+ {
+ const void *ptr1, *ptr2;
+
+ if ( memcmp(sec1[i].name, sec2[i].name, sizeof(sec1[i].name)) ||
+ sec1[i].rva != sec2[i].rva ||
+ sec1[i].size != sec2[i].size ||
+ sec1[i].file_size != sec2[i].file_size ||
+ sec1[i].flags != sec2[i].flags )
+ {
+ fprintf(stderr, "Mismatched section %u parameters\n", i);
+ return 5;
+ }
+
+ if ( !sec1[i].size ||
+ (sec1[i].flags & (COFF_SECTION_DISCARDABLE|COFF_SECTION_BSS)) )
+ continue;
+
+ /*
+ * Don't generate relocations for sections that definitely
+ * aren't used by the boot loader code.
+ */
+ if ( memcmp(sec1[i].name, ".initcal", sizeof(sec1[i].name)) == 0 ||
+ memcmp(sec1[i].name, ".init.se", sizeof(sec1[i].name)) == 0 ||
+ memcmp(sec1[i].name, ".lockpro", sizeof(sec1[i].name)) == 0 )
+ continue;
+
+ if ( !sec1[i].rva )
+ {
+ fprintf(stderr, "Can't handle section %u with zero RVA\n", i);
+ return 3;
+ }
+
+ if ( sec1[i].file_size > sec1[i].size )
+ {
+ sec1[i].file_size = sec1[i].size;
+ sec2[i].file_size = sec2[i].size;
+ }
+ ptr1 = map_section(sec1 + i, in1, argv[1]);
+ ptr2 = map_section(sec2 + i, in2, argv[2]);
+
+ diff_sections(ptr1, ptr2, sec1 + i, base2 - base1, width1,
+ base1, base1 + size1);
+
+ unmap_section(ptr1, sec1 + i);
+ unmap_section(ptr2, sec2 + i);
+ }
+
+ diff_sections(NULL, NULL, NULL, 0, 0, 0, 0);
+
+ puts("__base_relocs_end:");
+
+ close(in1);
+ close(in2);
+
+ return 0;
+}